{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [注解就是这么简单](https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247484113&idx=1&sn=f5fd35b2f7dc45a256fee170cad0cdc9&chksm=ebd743d0dca0cac6a8fed8caf09f0d561b0358dece9b8e746e1e6726bca093287cf515638349&scene=21###wechat_redirect)\n",
    "现在的开发都推崇使用注解来进行开发，这样就可以免去写XML配置了，十分方便的一项技术～\n",
    "\n",
    "学习注解可以更好地理解注解是怎么工作的，看见注解了就可以想到它的运行原理了～。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 什么是注解？\n",
    "注解(Annotation)\n",
    "\n",
    "注解其实就是代码中的特殊标记，这些标记可以在编译、类加载、运行时被读取，并执行相对应的处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 为什么我们需要用到注解？\n",
    "传统的方式，我们是通过`配置文件(xml文件)来告诉类是如何运行的`。\n",
    "\n",
    "有了注解技术以后，我们就可以`通过注解告诉类如何运行`\n",
    "\n",
    "例如：我们以前编写Servlet的时候，需要在web.xml文件配置具体的信息\n",
    "```xml\n",
    "<servlet>\n",
    "    <servlet-name>Refresh</servlet-name>\n",
    "    <servlet-class>Refresh</servlet-class>\n",
    "    <load-on-startup>1</load-on-startup>\n",
    "</servlet>\n",
    "<servlet-mapping>\n",
    "    <servlet-name>Refresh</servlet-name>\n",
    "    <url-pattern>/Refresh</url-pattern>\n",
    "</servlet-mapping>\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们使用了注解以后，可以直接在Servlet源代码上，增加注解…Servlet就被配置到Tomcat上了。也就是说，**注解可以给类、方法上注入信息。**\n",
    "```java\n",
    "@WebServlet(name = \"BuyServlet\", urlPatterns = \"/BuyServlet\")\n",
    "public class BuyServlet extends HttpServlet{\n",
    "\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "明显地可以看出，这样是非常直观的，并且**Servlet规范是推崇这种配置方式的。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基本Annotation\n",
    "在java.lang包下存在着5个基本的Annotation，其中有3个Annotation我们是非常常见的了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### @Override\n",
    "**重写注解**\n",
    "\n",
    "如果我们使用IDE重写父类的方法，我们就可以看见它了。那它有什么用呢？？\n",
    "\n",
    "@Overried是告诉编译器要检查**该方法是实现父类的**…可以帮我们避免一些低级的错误…\n",
    "\n",
    "比如，我们在实现equals()方法的时候，把euqals()打错了，那么**编译器就会发现该方法并不是实现父类的，与注解@Override冲突，于是就会给予错误。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### @Deprecated\n",
    "**过时注解**\n",
    "\n",
    "该注解也非常常见，Java在设计的时候，可能觉得某些方法设计得不好，**为了兼容以前的程序，是不能直接把它抛弃的，于是就设置它为过时。**\n",
    "\n",
    "Date对象中的toLocalString()就被设置成过时了\n",
    "```java\n",
    "@Deprecated\n",
    "public String toLocaleString(){\n",
    "    DateFormat formatter = DateFormat.getDateTimeInstance();\n",
    "    return formatter.format(this);\n",
    "}\n",
    "```\n",
    "当我们在程序中调用它的时候，在IDE上会出现一条横杠，说明该方法是过时的。\n",
    "\n",
    "Data date = new Date();\n",
    "\n",
    "date.~~toLocaleString~~();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### @SuppressWarnings\n",
    "**抑制编译器警告注解**\n",
    "\n",
    "该注解在我们写程序的时候并不是很常见，我们可以用它来**让编译器不给予我们警告**\n",
    "\n",
    "当我们在使用集合的时候，如果没有指定泛型，那么会提示安全检查的警告"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:02+0000",
     "start_time": "2020-08-25T12:02:04.028Z"
    }
   },
   "outputs": [],
   "source": [
    "//如果我们在类上添加了@SuppressWarnings这个注解，那么编译器就不会给予我们警告了\n",
    "@SuppressWarnings(\"unchecked\")\n",
    "public class Demo{\n",
    "    public static void main(){\n",
    "        ArrayList arrayList = new ArrayList();\n",
    "        System.out.println(arrayList);\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### @SafeVarargs\n",
    "**Java 7 \"堆污染\"警告**\n",
    "\n",
    "什么是堆污染呢？？**当把一个不是泛型的集合赋值给一个带泛型的集合的时候**，这种情况就很容易发生堆污染….\n",
    "\n",
    "这个注解也是用来抑制编译器警告的注解…用的地方并不多，……有用到的时候再回来填坑吧。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ＠FunctionalInterface\n",
    "**用来指定该接口是函数式接口**\n",
    "\n",
    "用该注解**显示指定**该接口是一个函数式接口。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 自定义注解基础\n",
    "上面讲解的是java.lang包下的5个注解，**我们可以自己来写注解，给方法或类注入信息。**\n",
    "\n",
    "### 标记Annotation\n",
    "**没有任何成员变量的注解称为标记注解，@Overried就是一个标记注解\n",
    "```java\n",
    "//有点像定义一个接口一样，只不过它多了一个@\n",
    "public @interface MyAnnotation{\n",
    "\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 元数据Annotation\n",
    "**我们自定义的注解是可以带成员变量的，定义带成员变量的注解叫做元数据Annotation**\n",
    "\n",
    "在注解中定义成员变量，**语法类似于声明方法一样**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:01+0000",
     "start_time": "2020-08-25T12:01:32.733Z"
    }
   },
   "outputs": [],
   "source": [
    "public @interface MyAnnotation{\n",
    "    //定义两个成员变量\n",
    "    String username();\n",
    "    int age();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意：在注解上**定义的成员变量只能是String、数组、Class、枚举类、注解**\n",
    "\n",
    "有的人可能会奇怪，为什么注解上还要定义注解成员变量？？听起来就很复杂了….\n",
    "\n",
    "上边已经说了，注解的作用就是给类、方法注入信息。那么我们经常使用XML文件，告诉程序怎么运行。**XML经常会有嵌套的情况**\n",
    "```xml\n",
    "<书>\n",
    "    <作者>警方</作者>\n",
    "    <价格>99</价格>\n",
    "</书>\n",
    "```\n",
    "那么，当我们在使用注解的时候，也可能需要有嵌套的时候，所以就**允许了注解上可以定义成员变量为注解。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用自定义注解\n",
    "#### 常规使用\n",
    "下面我有一个add的方法，需要username和age参数，我们**通过注解来让该方法拥有这两个变量！**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T10:18+0000",
     "start_time": "2020-08-25T10:18:00.737Z"
    }
   },
   "outputs": [],
   "source": [
    "//注解拥有什么属性，在修饰的时候就要给出相应的值\n",
    "@MyAnnotation(username = \"anlzou\", age = 20)\n",
    "public static void add(String username, int age){\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 默认值\n",
    "**可以在注解声明属性的时候，给出默认值**。那么在修饰的时候，就可以不用具体指定了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:01+0000",
     "start_time": "2020-08-25T12:01:40.851Z"
    }
   },
   "outputs": [],
   "source": [
    "public @interface MyAnnotation{\n",
    "    //定义了两个变量\n",
    "    String username() default \"anlzou\";\n",
    "    int age() default 20;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T10:20+0000",
     "start_time": "2020-08-25T10:20:18.638Z"
    }
   },
   "source": [
    "- 在修饰的时候就不需要给出具体的值了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T10:24+0000",
     "start_time": "2020-08-25T10:24:09.508Z"
    }
   },
   "outputs": [],
   "source": [
    "@MyAnnotation()\n",
    "public void add(String username, int age){\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 注解属性为value\n",
    "还有一种特殊的情况，如果**注解上只有一个属性，并且属性的名称为value**，那么在使用的时候，我们**可以不写value，直接赋值给它就行**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T10:25+0000",
     "start_time": "2020-08-25T10:25:09.479Z"
    }
   },
   "outputs": [],
   "source": [
    "public @interface MyAnnotation2{\n",
    "    String value();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 使用注解，可以**不指定value，直接赋值**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T10:26+0000",
     "start_time": "2020-08-25T10:26:32.349Z"
    }
   },
   "outputs": [],
   "source": [
    "@MyAnnotation2(\"anlzou\")\n",
    "public void find(String id){\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 把自定义注解的基本信息注入到方法上\n",
    "上面我们已经使用到了注解，但是目前为止**注解上的信息和方法上的信息是没有任何关联的。**\n",
    "\n",
    "我们使用Servlet注解的时候，仅仅调用注解，注解就生效了。这是**Web容器把内部实现了**。我们**自己写的自定义注解是需要我们自己来处理的。**\n",
    "\n",
    "那现在问题来了，怎么把注解上的信息注入到方法上呢？**利用的是反射技术**\n",
    "\n",
    "步骤可分为三步：\n",
    "- **反射出该类的方法**\n",
    "- **通过方法得到注解上具体的信息**\n",
    "- **将注解上的信息注入到方法上**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:03+0000",
     "start_time": "2020-08-25T12:03:54.490Z"
    }
   },
   "outputs": [
    {
     "ename": "CompilationException",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30mClass relClass = \u001b[0m\u001b[1m\u001b[30m\u001b[41mDemo2\u001b[0m\u001b[1m\u001b[30m.class;\u001b[0m",
      "\u001b[1m\u001b[31mcannot find symbol\u001b[0m",
      "\u001b[1m\u001b[31m  symbol:   class Demo2\u001b[0m",
      ""
     ]
    }
   ],
   "source": [
    "//反射出该类的方法\n",
    "Class relClass = Demo2.class;\n",
    "Method method = relClass.getMethod(\"add\", String.class, int.class);\n",
    "\n",
    "//通过该方法得到注解上的具体信息\n",
    "MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);\n",
    "String username = annotation.username();\n",
    "int age = annotation.age();\n",
    "\n",
    "//将注解上的信息注入到方法上\n",
    "Object o = relClass.newInstance();\n",
    "method.invoke(o, username, age);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此时，我们需要在自定义注解上加入这样一句代码(下面就会讲到，为什么要加入这句代码)\n",
    "```java\n",
    "@Retention(RetentionPolicy.RUNTIME)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## JDK的元Annotaion\n",
    "前面我们已经介绍了java.lang包下的几个基本Annotation了。在JDK中除了java.lang包下有Annotation，**在java.lang.annotation下也有几个常用的元Annotation。**\n",
    "\n",
    "在annotation包下的好几个元Annotation都是**用于修饰其他的Annotation定义。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### @Retention\n",
    "上面在将注解信息注入到方法中的时候，我们**最后加上了@Retention的注解….不然就会报错了**..那它是干什么用的呢？\n",
    "\n",
    "@Retention只能用于修饰其他的Annotation，**用于指定被修饰的Annotation被保留多长时间。**\n",
    "\n",
    "@Retention **包含了一个RetentionPolicy类型的value变量**，所以在使用它的时候，**必须要为value成员变量赋值**\n",
    "\n",
    "value变量的值只有三个："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```java\n",
    "public enum RetentionPolicy{\n",
    "    /**\n",
    "     * Annotations are to be discarded by the compiler.\n",
    "     */\n",
    "    SOURCE,\n",
    "\n",
    "    /**\n",
    "     * Annotations are to be recorded in the class file by the compiler\n",
    "     * but need not be retained by the VM at run time.  This is the default\n",
    "     * behavior.\n",
    "     */\n",
    "    CLASS,\n",
    "\n",
    "    /**\n",
    "     * Annotations are to be recorded in the class file by the compiler and\n",
    "     * retained by the VM at run time, so they may be read reflectively.\n",
    "     *\n",
    "     * @see java.lang.reflect.AnnotatedElement\n",
    "     */\n",
    "    RUNTIME\n",
    "}\n",
    "```\n",
    "java文件有三个时期：**编译，class，运行。@Retention默认是class。**\n",
    "\n",
    "前面我们是使用反射来得到注解上的信息的，**因为@Retention默认是class，而反射是在运行时期来获取信息的。因此就获取不到Annotation的信息了**。于是，就得在自定义注解上修改它的RetentionPolicy值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### @Target\n",
    "@Target也是**只能用于修饰另外的Annotation，它用于指定被修饰的Annotation用于修饰哪些程序单元**\n",
    "\n",
    "@Target是只有一个value成员变量的，该成员变量的值是以下的："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```java\n",
    "public enum ElementType {\n",
    "    /** Class, interface (including annotation type), or enum declaration */\n",
    "    TYPE,\n",
    "\n",
    "    /** Field declaration (includes enum constants) */\n",
    "    FIELD,\n",
    "\n",
    "    /** Method declaration */\n",
    "    METHOD,\n",
    "\n",
    "    /** Parameter declaration */\n",
    "    PARAMETER,\n",
    "\n",
    "    /** Constructor declaration */\n",
    "    CONSTRUCTOR,\n",
    "\n",
    "    /** Local variable declaration */\n",
    "    LOCAL_VARIABLE,\n",
    "\n",
    "    /** Annotation type declaration */\n",
    "    ANNOTATION_TYPE,\n",
    "\n",
    "    /** Package declaration */\n",
    "    PACKAGE\n",
    "}\n",
    "```\n",
    "如果@Target指定的是ElementType.ANNOTATION_TYPE，那么**该被修饰的Annotation只能修饰Annotaion**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### @Documented\n",
    "@Documented用于指定**被改Annotation修饰的Annotation类将被javadoc工具提取成文档**。该注解用得少。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ＠Inherited\n",
    "@Inherited也是用来修饰其他的Annotation的，被修饰过的Annotation将具有继承性。\n",
    "\n",
    "例子：\n",
    "\n",
    "1. @xxx是我自定义的注解，我现在使用@xxx注解在Base类上使用…\n",
    "2. 使用@Inherited修饰@xxx注解\n",
    "3. 当有类继承了Base类的时候，该实现类自动拥有@xxx注解"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 注入对象到方法或成员变量上\n",
    "### 把对象注入到方法上\n",
    "前面已经可以使用注解将基本的信息注入到方法上了，现在我们要使用的是**将对象注入到方法上**\n",
    "\n",
    "上边已经说过了，注解上只能定义String、枚举类、Double之类的成员变量，那怎么把对象注入到方法上呢？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 模拟场景\n",
    "－ Person类，定义username和age属性，拥有uername和age的getter和setter方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:55+0000",
     "start_time": "2020-08-25T12:55:02.942Z"
    }
   },
   "outputs": [],
   "source": [
    "public class Person{\n",
    "    private String username;\n",
    "    private int age;\n",
    "    \n",
    "    public String getUsername(){\n",
    "        return username;\n",
    "    }\n",
    "    \n",
    "    public void setUsername(String username){\n",
    "        this.username = username;\n",
    "    }\n",
    "    \n",
    "    public int getAge(){\n",
    "        return age;\n",
    "    }\n",
    "    \n",
    "    public void setAge(int age){\n",
    "        this.age = age;\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- PersionDao类，PersonDao类定义了Persion对象，拥有persion的setter和getter方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:55+0000",
     "start_time": "2020-08-25T12:55:07.161Z"
    }
   },
   "outputs": [],
   "source": [
    "public class PersonDao{\n",
    "    private Person person;\n",
    "    \n",
    "    public Person getPerson(){\n",
    "        return person;\n",
    "    }\n",
    "    \n",
    "    public void setPerson(Person person){\n",
    "        this.person = person;\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 现在要做的就是：**使用注解将Person对象注入到setPerson()方法中，从而设置了PersonDao类的person属性**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:42+0000",
     "start_time": "2020-08-25T12:42:41.293Z"
    }
   },
   "outputs": [
    {
     "ename": "CompilationException",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30mpublic class PersonDao{\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    private Person person;\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    \u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    public Person getPerson(){\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m        return person;\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    }\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    \u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    //将username为anlzou,age为20的Person对象注入setPerson方法中\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    @InjectPerson(username = \"anlzou\", age = 20)\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    public void setPerson(Person person){\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m        this.person = person;\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    }\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m}\u001b[0m",
      "\u001b[1m\u001b[31mUnresolved dependencies:\u001b[0m",
      "\u001b[1m\u001b[31m   - class InjectPerson\u001b[0m"
     ]
    }
   ],
   "source": [
    "public class PersonDao{\n",
    "    private Person person;\n",
    "    \n",
    "    public Person getPerson(){\n",
    "        return person;\n",
    "    }\n",
    "    \n",
    "    //将username为anlzou,age为20的Person对象注入setPerson方法中\n",
    "    @InjectPerson(username = \"anlzou\", age = 20)\n",
    "    public void setPerson(Person person){\n",
    "        this.person = person;\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**步骤：**\n",
    "\n",
    "1. 定义一个注解，属性是和JavaBean类一致的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:48+0000",
     "start_time": "2020-08-25T12:48:11.255Z"
    }
   },
   "outputs": [
    {
     "ename": "CompilationException",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m//注入工具是通过反射得到注解的信息，保留域必须用RunTime\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m@Retention(RetentionPolicy.RUNTIME)\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30mpublic @interface InjectPerson{\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    String username();\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    int age();\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m}\u001b[0m",
      "\u001b[1m\u001b[31mUnresolved dependencies:\u001b[0m",
      "\u001b[1m\u001b[31m   - class Retention\u001b[0m",
      "\u001b[1m\u001b[31m   - variable RetentionPolicy\u001b[0m"
     ]
    }
   ],
   "source": [
    "//注入工具是通过反射得到注解的信息，保留域必须用RunTime\n",
    "@Retention(RetentionPolicy.RUNTIME)\n",
    "public @interface InjectPerson{\n",
    "    String username();\n",
    "    int age();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 编写注入工具"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:55+0000",
     "start_time": "2020-08-25T12:55:12.252Z"
    }
   },
   "outputs": [
    {
     "ename": "CompilationException",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m\u001b[0m\u001b[1m\u001b[30m\u001b[41mPropertyDescriptor\u001b[0m\u001b[1m\u001b[30m descriptor = new PropertyDescriptor(\"person\", PersonDao.class);\u001b[0m",
      "\u001b[1m\u001b[31mcannot find symbol\u001b[0m",
      "\u001b[1m\u001b[31m  symbol:   class PropertyDescriptor\u001b[0m",
      "",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30mPropertyDescriptor descriptor = new \u001b[0m\u001b[1m\u001b[30m\u001b[41mPropertyDescriptor\u001b[0m\u001b[1m\u001b[30m(\"person\", PersonDao.class);\u001b[0m",
      "\u001b[1m\u001b[31mcannot find symbol\u001b[0m",
      "\u001b[1m\u001b[31m  symbol:   class PropertyDescriptor\u001b[0m",
      ""
     ]
    }
   ],
   "source": [
    "//1.使用内省【后边需要得到属性的写方法】，得到想要注入的属性\n",
    "PropertyDescriptor descriptor = new PropertyDescriptor(\"person\", PersonDao.class);\n",
    "\n",
    "//2.得到要想注入属性的具体对象\n",
    "Person person = (Person) descriptor.getPropertyType().newInstance();\n",
    "\n",
    "//3.得到该属性的写方法【setPerson()】\n",
    "Method method = descriptor.getWriteMethod();\n",
    "\n",
    "//4.得到写方法的注解\n",
    "Annotation annotation = method.getAnnotation(InjectPerson.class);\n",
    "\n",
    "//5.得到注解上的信息【注解的成员变量就是用方法来定义的】\n",
    "Method[] methods = annotation.getClass().getMethods();\n",
    "\n",
    "//6.将注解上的信息填充到person对象上\n",
    "\n",
    "for (Method m : methods) {\n",
    "\n",
    "    //得到注解上属性的名字【age或name】\n",
    "    String name = m.getName();\n",
    "\n",
    "    //看看Person对象有没有与之对应的方法【setAge(),setName()】\n",
    "    try {\n",
    "\n",
    "        //6.1这里假设：有与之对应的写方法，得到写方法\n",
    "        PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Person.class);\n",
    "        Method method1 = descriptor1.getWriteMethod();//setAge(), setName()\n",
    "\n",
    "        //得到注解中的值\n",
    "        Object o = m.invoke(annotation, null);\n",
    "\n",
    "        //调用Person对象的setter方法，将注解上的值设置进去\n",
    "        method1.invoke(person, o);\n",
    "\n",
    "    } catch (Exception e) {\n",
    "\n",
    "        //6.2 Person对象没有与之对应的方法，会跳到catch来。我们要让它继续遍历注解就好了\n",
    "        continue;\n",
    "    }\n",
    "}\n",
    "\n",
    "//当程序遍历完之后，person对象已经填充完数据了\n",
    "\n",
    "//7.将person对象赋给PersonDao【通过写方法】\n",
    "PersonDao personDao = new PersonDao();\n",
    "method.invoke(personDao, person);\n",
    "\n",
    "System.out.println(personDao.getPerson().getUsername());\n",
    "System.out.println(personDao.getPerson().getAge());"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3. 总结一下步骤\n",
    "\n",
    "其实我们是这样把对象注入到方法中的：\n",
    "\n",
    "- 得到想要类中注入的属性\n",
    "- 得到该属性的对象\n",
    "- 得到属性对应的写方法\n",
    "- 通过写方法得到注解\n",
    "- 获取注解详细的信息\n",
    "- 将注解的信息注入到对象上\n",
    "- 调用属性写方法，将已填充数据的对象注入到方法中"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 把对象注入到成员变量\n",
    "上面已经说了如何将对象注入到方法上了，那么注入到成员变量上也是非常简单的。\n",
    "\n",
    "**步骤：**\n",
    "\n",
    "1. 在成员变量上使用注解"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:57+0000",
     "start_time": "2020-08-25T12:57:53.953Z"
    }
   },
   "outputs": [
    {
     "ename": "CompilationException",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30mpublic class PersonDao {\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    @InjectPerson(username = \"zhongfucheng\",age = 20) private Person person;\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    public Person getPerson() {\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m        return person;\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    }\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    public void setPerson(Person person) {\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m        this.person = person;\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    }\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m}\u001b[0m",
      "\u001b[1m\u001b[31mUnresolved dependencies:\u001b[0m",
      "\u001b[1m\u001b[31m   - class InjectPerson\u001b[0m"
     ]
    }
   ],
   "source": [
    "public class PersonDao {\n",
    "\n",
    "    @InjectPerson(username = \"zhongfucheng\",age = 20) private Person person;\n",
    "\n",
    "    public Person getPerson() {\n",
    "        return person;\n",
    "    }\n",
    "\n",
    "    public void setPerson(Person person) {\n",
    "\n",
    "        this.person = person;\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 编写注入工具"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-08-25T12:59+0000",
     "start_time": "2020-08-25T12:59:06.767Z"
    }
   },
   "outputs": [
    {
     "ename": "CompilationException",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m\u001b[0m\u001b[1m\u001b[30m\u001b[41mField\u001b[0m\u001b[1m\u001b[30m field = PersonDao.class.getDeclaredField(\"person\");\u001b[0m",
      "\u001b[1m\u001b[31mcannot find symbol\u001b[0m",
      "\u001b[1m\u001b[31m  symbol:   class Field\u001b[0m",
      "",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30mField field = \u001b[0m\u001b[1m\u001b[30m\u001b[41mPersonDao\u001b[0m\u001b[1m\u001b[30m.class.getDeclaredField(\"person\");\u001b[0m",
      "\u001b[1m\u001b[31mcannot find symbol\u001b[0m",
      "\u001b[1m\u001b[31m  symbol:   class PersonDao\u001b[0m",
      ""
     ]
    }
   ],
   "source": [
    "//1.得到想要注入的属性\n",
    "Field field = PersonDao.class.getDeclaredField(\"person\");\n",
    "\n",
    "//2.得到属性的具体对象\n",
    "Person person = (Person) field.getType().newInstance();\n",
    "\n",
    "//3.得到属性上的注解\n",
    "Annotation annotation = field.getAnnotation(InjectPerson.class);\n",
    "\n",
    "//4.得到注解的属性【注解上的属性使用方法来表示的】\n",
    "Method[] methods = annotation.getClass().getMethods();\n",
    "\n",
    "//5.将注入的属性填充到person对象上\n",
    "for (Method method : methods) {\n",
    "\n",
    "    //5.1得到注解属性的名字\n",
    "    String name = method.getName();\n",
    "\n",
    "    //查看一下Person对象上有没有与之对应的写方法\n",
    "    try {\n",
    "\n",
    "        //如果有\n",
    "        PropertyDescriptor descriptor = new PropertyDescriptor(name, Person.class);\n",
    "\n",
    "        //得到Person对象上的写方法\n",
    "        Method method1 = descriptor.getWriteMethod();\n",
    "\n",
    "        //得到注解上的值\n",
    "        Object o = method.invoke(annotation, null);\n",
    "\n",
    "        //填充person对象\n",
    "        method1.invoke(person, o);\n",
    "    } catch (IntrospectionException e) {\n",
    "\n",
    "        //如果没有想对应的属性，继续循环\n",
    "        continue;\n",
    "    }\n",
    "}\n",
    "\n",
    "//循环完之后，person就已经填充好数据了\n",
    "\n",
    "\n",
    "//6.把person对象设置到PersonDao中\n",
    "PersonDao personDao = new PersonDao();\n",
    "field.setAccessible(true);\n",
    "field.set(personDao, person);\n",
    "\n",
    "System.out.println(personDao.getPerson().getUsername());"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结\n",
    "1. 注入对象的步骤：**得到想要注入的对象属性，通过属性得到注解的信息，通过属性的写方法将注解的信息注入到对象上，最后将对象赋给类。**\n",
    "\n",
    "2.  注解其实是两个作用：\n",
    "－ **让编译器检查代码**\n",
    "－ **将数据注入到方法、成员变量、类上**\n",
    "\n",
    "3. 在JDK中注解分为了\n",
    "\n",
    "- 基本Annotation\n",
    "    - 在lang包下，用于常用于标记该方法，抑制编译器警告等\n",
    "- 元Annotaion\n",
    "    - 在annotaion包下，常用于修饰其他的Annotation定义"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Java",
   "language": "java",
   "name": "java"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "Java",
   "pygments_lexer": "java",
   "version": "11.0.8+10-post-Ubuntu-0ubuntu120.04"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
